/*
 * Visualizer.c
 *
 *  Created on: 06.06.2011
 *      Author: sili
 */

#include "Visualizer.h"
#include "AudioFactory.h"
#include "AALItem.h"
#include "PropertyFactory.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("at.sili.aalsystem.visualization.Visualizer"));

static const char *OsdErrorTexts[] = {
  "ok",
  "too many areas",
  "too many colors",
  "bpp not supported",
  "areas overlap",
  "wrong alignment",
  "out of memory",
  "wrong area size",
  "unknown",
  };

Visualizer::Visualizer(VisFactory * _visFactory) {
	AAL_DEBUG(logger, "Constructor");

	this->visFactory = _visFactory;
	this->exitRequested = false;

	cOsdProvider::UpdateOsdSize(true);

	this->visualizerArea.left = cOsd::OsdLeft();
	this->visualizerArea.top = cOsd::OsdTop();
	this->visualizerArea.width = cOsd::OsdWidth();
	this->visualizerArea.height = cOsd::OsdHeight();

	AAL_DEBUG(logger, "Creating areas. visualizer AALArea: left:%f top:%f width:%f height:%f",
			this->visualizerArea.left, this->visualizerArea.top, this->visualizerArea.width, this->visualizerArea.height);
	this->osd = cOsdProvider::NewOsd(this->visualizerArea.left, this->visualizerArea.top, 50);

	// calculate content and info draw pane sizes
	const cFont * font = cFont::GetFont(fontOsd);
	int h = font->Height("A");

	int totalHeight = this->visualizerArea.height - 2;
	int totalWidth = this->visualizerArea.width - 2;

	AALArea contentArea;
	contentArea.left = 0;
	contentArea.top = 0;
	contentArea.width = totalWidth;
	contentArea.height = totalHeight * 0.7;

	int y = contentArea.top + contentArea.height + 1;

	AALArea infoArea;		// at the middle
	infoArea.left = 0;
	infoArea.top = y;
	infoArea.width = totalWidth;
	infoArea.height = (totalHeight - y ) - (h + 2);

	y = infoArea.top + infoArea.height + 1;

	AALArea controlArea;	// at the bottom
	controlArea.left = 0;
	controlArea.top = y;
	controlArea.width = totalWidth;
	controlArea.height = (totalHeight - y);

	if(cOsdProvider::SupportsTrueColor()){
		AAL_DEBUG(logger, "Using trueColor osd");
		// create only one area !
		tArea tArea = {0, 0, this->visualizerArea.width, this->visualizerArea.height, 32};
		if (this->osd->SetAreas(&tArea, 1) == oeOk) {
			cPixmap::Lock();
			cPixmap * contentPixmap = osd->CreatePixmap(5,cRect(contentArea.left, contentArea.top, contentArea.width, contentArea.height));
			this->contentPane = new DrawPane(contentPixmap, &contentArea, 0x00000000, DrawPane::contentPaneType,"contentPane");
			cPixmap * infoPixmap = osd->CreatePixmap(6,cRect(infoArea.left, infoArea.top, infoArea.width, infoArea.height));
			this->infoPane = new DrawPane(infoPixmap, &infoArea, 0x800000FF,DrawPane::infoPaneType,"infoPane");
			cPixmap * controlPixmap = osd->CreatePixmap(7,cRect(controlArea.left, controlArea.top, controlArea.width, controlArea.height));
			this->controlPane = new ControlDrawPane(controlPixmap, &controlArea, 0x80000000);
			cPixmap::Unlock();
		} else{
			AAL_DEBUG(logger, "Can't set area");
		}
		this->osdAsImage = NULL;
	} else {
		AAL_DEBUG(logger, "Can't use trueColor osd");
		// create 3 areas !
		//						 x1					y1					x2					y2
		tArea usedTAreas[] = {	{contentArea.left,	contentArea.top,	contentArea.width,	contentArea.top + contentArea.height, 2},
								{infoArea.left, 	infoArea.top, 		infoArea.width, 	infoArea.top + infoArea.height, 2},
								{controlArea.left, 	controlArea.top, 	controlArea.width, 	controlArea.top + controlArea.height, 2},
		};

		int possibleBpps[6] = {2,4,8,16,24,32};
		for(int index=0; index<6; index++){
			int usedBpp = possibleBpps[index];
			//						 x1					y1					x2					y2
			tArea areas[] = {	{contentArea.left,	contentArea.top,	contentArea.width,	contentArea.top + contentArea.height, 	usedBpp},
								{infoArea.left, 	infoArea.top, 		infoArea.width, 	infoArea.top + infoArea.height, 	usedBpp},
								{controlArea.left, 	controlArea.top, 	controlArea.width, 	controlArea.top + controlArea.height, 	usedBpp},
			};
			if (this->osd->CanHandleAreas(areas, sizeof(areas) / sizeof(tArea)) == oeOk){
				AAL_DEBUG(logger, "I can use %d BPP", usedBpp);
				usedTAreas[0].bpp  = usedBpp;
				usedTAreas[1].bpp  = usedBpp;
				usedTAreas[2].bpp  = usedBpp;
			}
		}
		AAL_DEBUG(logger, "Using %d BPP", usedTAreas[0].bpp);
		eOsdError setState = this->osd->SetAreas(usedTAreas, sizeof(usedTAreas) / sizeof(tArea));
		if (setState == oeOk) {
			cPixmap::Lock();
			this->contentPane = new DrawPane(usedTAreas[0].bpp, &contentArea, 0x00000000, DrawPane::contentPaneType, "contentPane");
			this->infoPane = new DrawPane(usedTAreas[0].bpp, &infoArea, 0x800000FF, DrawPane::infoPaneType, "infoPane");
			this->controlPane = new ControlDrawPane(usedTAreas[0].bpp, &controlArea, 0x80000000);
			cPixmap::Unlock();
		} else{
			AAL_DEBUG(logger, "Can't set areas because of %s", OsdErrorTexts[setState]);
		}
		this->bpp = usedTAreas[0].bpp;
	}

	// Reset origin because it was used to set up the osd.
	// From now on we use it relative to this created osd areas.
	this->visualizerArea.left = 0;
	this->visualizerArea.top = 0;

	if(!cOsdProvider::SupportsTrueColor()){
		Geometry geometry(this->visualizerArea.width, this->visualizerArea.height, this->visualizerArea.left, this->visualizerArea.top, false, false);
		// here	    r    g    b    a
		Color color(0,0,0,TransparentOpacity);
		this->osdAsImage = new Image(geometry,color);
		this->osdAsImage->type(TrueColorMatteType);
	}
}

Visualizer::~Visualizer(){
	AAL_DEBUG(logger, "Destructor");

	// I'm gonna to die, so systemfactory need to create another visualizer
	this->visFactory->destroyVisualizer();

	delete(this->osd);
	delete(this->contentPane);
	delete(this->infoPane);
	delete(this->controlPane);
	delete(this->osdAsImage);
}

DrawPane * Visualizer::getInfoPane(){
	return this->infoPane;
}

DrawPane * Visualizer::getContentPane(){
	return this->contentPane;
}

ControlDrawPane * Visualizer::getControlPane(){
	return this->controlPane;
}

uint32_t Visualizer::getTextColor(){
	// default text color == white
	uint32_t color = 0xFFFFFFFF;

	// get the user setting if any exists
	const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_TEXT);
	if(argbColor != NULL){
		char * end_ptr;
		color = strtoul(argbColor,&end_ptr, 16);
	}
	return color;
}

uint32_t Visualizer::getBackgroundColorForPane(DrawPane::PaneType _paneType){
	uint32_t color = 0x00000000;
	if(_paneType == DrawPane::contentPaneType){
		// default color content pane == (transparent)
		color = 0xFF000000;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_CONTENTPANE);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	} else if(_paneType == DrawPane::infoPaneType){
		// default color info pane == black (transparent)
		color = 0x80000000;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_TEXTPANE);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	} else if(_paneType == DrawPane::controlPaneType){
		// default color control pane == black (transparent)
		color = 0x80000000;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_CONTROLPANE);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	}
	return color;
}

uint32_t Visualizer::getBackgroundColorForAction(AALAction * _action){

	uint32_t color = 0x00000000;

	if(_action->type == doInfo){
		// default colorInfo == blue (transparent)
		color = 0x800000FF;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_SIGNALINFO);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	} else if(_action->type == doWarn){
		// default colorWarn == orange (transparent)
		color = 0x80FFA500;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_SIGNALWARN);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	} else if(_action->type == doAlarm){
		// default colorAlarm == red (transparent)
		color = 0x80FF0000;

		// get the user setting if any exists
		const char * argbColor = this->visFactory->getController()->getPropertyFactory()->getStringProperty(AALPROP_ARGBCOLOR_SIGNALALARM);
		if(argbColor != NULL){
			char * end_ptr;
			color = strtoul(argbColor,&end_ptr, 16);
		}
	}

	return color;
}

static pthread_rwlock_t visLock = PTHREAD_RWLOCK_INITIALIZER;

void Visualizer::Show(void){
	pthread_rwlock_rdlock(&visLock);
	AALAction * nextAction = this->visFactory->getNextAction();
	if (this->osd && nextAction) {
		AAL_DEBUG(logger, "ActionId: %ld actionType: %s", nextAction->id, nextAction->typeToString());

		// THE EXIT OR BACK CONTROL
		if(getControlPane() != NULL){
			AAL_DEBUG(logger, "Drawing controlPane...");
			uint32_t bgColor = getBackgroundColorForPane(DrawPane::controlPaneType);
			getControlPane()->colorBg = bgColor;

			getControlPane()->Clear();

			const cFont * font = cFont::GetFont(fontOsd);
			if(this->visFactory->getNumOfActions() == 1){
				this->controlPane->drawControl(0,tr("Verlassen"), clrWhite, clrRed, font, DrawPane::taCenter);
			} else {
				this->controlPane->drawControl(0,"Zur%C3%BCck", clrWhite, clrRed, font, DrawPane::taCenter);
				this->controlPane->drawControl(3,"Verlassen", clrWhite, clrBlue, font, DrawPane::taCenter);
			}
		}

		if(this->contentPane){
			AAL_DEBUG(logger, "Drawing contentPane...");
			this->contentPane->currentVisibleItems.clear();
		}
		if(this->infoPane){
			AAL_DEBUG(logger, "Drawing infoPane...");
			this->infoPane->currentVisibleItems.clear();
		}

		nextAction->aalItem->visualizeSelf(this, nextAction);

		if(this->osdAsImage != NULL){
			this->osdAsImage->erase();
			this->osdAsImage->composite(*this->contentPane->imagePane, (int)this->contentPane->area.left, (int)this->contentPane->area.top, OverCompositeOp);
			this->osdAsImage->composite(*this->infoPane->imagePane, (int)this->infoPane->area.left, (int)this->infoPane->area.top, OverCompositeOp);
			this->osdAsImage->composite(*this->controlPane->imagePane, (int)this->controlPane->area.left, (int)this->controlPane->area.top, OverCompositeOp);
			// osdAsImage->write("/var/lib/vdr/plugins/vdr4aal/unquantized.png");

			try {
				// this->osdAsImage->quantizeColors(pow(2,this->bpp)-10);	// <- This throws an exception (everything > 25 throws an exception on reelbox)
				// this->osdAsImage->quantizeColors(250);						// <- This is OK!

				this->osdAsImage->magick("RGB");
				//this->osdAsImage->quantizeTreeDepth(1);
				// this->osdAsImage->opacity(OpaqueOpacity);
				// this->osdAsImage->backgroundColor(Color(0, 0, 0, 0) );
				// this->osdAsImage->quantizeColorSpace(RGBColorspace);
				// this->osdAsImage->backgroundColor(Color(0, 0, 0, 0) );
				this->osdAsImage->quantizeColorSpace(TransparentColorspace);
				// this->osdAsImage->opacity(TransparentOpacity);
				// this->osdAsImage->quantizeDither(1);
				this->osdAsImage->quantizeColors(250);
				this->osdAsImage->quantize();

				/*
				this->osdAsImage->quantizeColorSpace( RGBColorspace );
				this->osdAsImage->backgroundColor( Color ( 0,0,0,0) );
				this->osdAsImage->quantize();
				*/
			} catch (...) {
				// cout << "Problem mit quantizierung ......................................" << endl;
			}

			//osdAsImage->write("/home/reel/quantized.png");

			cPalette p1;
			this->osd->SetPalette(p1,0);
			cPalette p2;
			this->osd->SetPalette(p2,1);
			cPalette p3;
			this->osd->SetPalette(p3,2);

			//cBitmap img(this->visualizerArea.width, this->visualizerArea.height, this->bpp, 0, 0);
			cBitmap img(this->visualizerArea.width, this->visualizerArea.height, 8, 0, 0);

			img.SetColor(0,0x00000000);
			img.SetColor(1,0x00000000);
			img.SetColor(2,this->contentPane->colorBg);
			img.SetColor(3,this->infoPane->colorBg);
			img.SetColor(4,this->controlPane->colorBg);

			for (unsigned int x = 0; x < osdAsImage->columns(); x++){
				for (unsigned int y = 0; y < osdAsImage->rows(); y++) {
					const PixelPacket * ppp = this->osdAsImage->getConstPixels(x, y, 1, 1);

					// Color replacement...
					// uint32_t tColor = ArgbToColor(0xFF,ppp->red,ppp->green,ppp->blue);
					uint32_t tColor = ArgbToColor(ppp->opacity,ppp->red,ppp->green,ppp->blue);

					if(this->contentPane->Intersect(x,y)){
						int a = 0x000000FF & this->contentPane->colorBg >> 24;
						int r = 0x000000FF & this->contentPane->colorBg >> 16;
						int g = 0x000000FF & this->contentPane->colorBg >> 8;
						int b = 0x000000FF & this->contentPane->colorBg;
						uint32_t bgnd = ArgbToColor(a,r,g,b);
						if(bgnd != tColor){
							tColor = ArgbToColor(0xFF,ppp->red,ppp->green,ppp->blue);
							//tColor = ArgbToColor(0x90,r,g,b);
						}
					} else if(this->infoPane->Intersect(x,y)){
						int a = 0x000000FF & this->infoPane->colorBg >> 24;
						int r = 0x000000FF & this->infoPane->colorBg >> 16;
						int g = 0x000000FF & this->infoPane->colorBg >> 8;
						int b = 0x000000FF & this->infoPane->colorBg;
						uint32_t bgnd = ArgbToColor(a,r,g,b);
						if(bgnd != tColor){
							tColor = ArgbToColor(0xFF,ppp->red,ppp->green,ppp->blue);
							//tColor = ArgbToColor(0x90,r,g,b);
						}
					} else if(this->controlPane->Intersect(x,y)){
						int a = 0x000000FF & this->controlPane->colorBg >> 24;
						int r = 0x000000FF & this->controlPane->colorBg >> 16;
						int g = 0x000000FF & this->controlPane->colorBg >> 8;
						int b = 0x000000FF & this->controlPane->colorBg;
						uint32_t bgnd = ArgbToColor(a,r,g,b);
						if(bgnd != tColor){
							tColor = ArgbToColor(0xFF,ppp->red,ppp->green,ppp->blue);
							//tColor = ArgbToColor(0x90,r,g,b);
						}
					}
					img.DrawPixel(x, y, tColor);
					// img.DrawPixel(x,y,ArgbToColor(ppp->opacity,ppp->red,ppp->green,ppp->blue));
					// this->osd->DrawPixel(x,y,tColor);
				}
			}
			this->osd->DrawBitmap(this->visualizerArea.left, this->visualizerArea.top, img, true, false);
		}
		this->osd->Flush();

		// Visualization took a while. Now we are ready and may create an audio action.
		if(nextAction->type == doInfo ||
		   nextAction->type == doWarn ||
		   nextAction->type == doAlarm) {
			AALAction * playAudioAction = new AALAction(doPlayAudio, nextAction->aalItem, nextAction->message.c_str(), 0, this->visFactory->getController());
			nextAction->children.push_back(playAudioAction);
			this->visFactory->getController()->publish(playAudioAction);
		}
	} else {
		AAL_ERROR(logger, "Can't get osd");
	}
	pthread_rwlock_unlock(&visLock);
}

void Visualizer::Exit(){
	if(!this->exitRequested){
		this->exitRequested = true;
		cRemote::Put(kStop);
	}
}

eOSState Visualizer::ProcessKey(eKeys Key){

	AALAction * nextAction = this->visFactory->getNextAction();

	if(nextAction == NULL){
		//if(this->currentVisAction == NULL)
		return osEnd;
	}

	eOSState state = osContinue;
	state = nextAction->aalItem->guiAction(Key);
	if(state == osContinue || state == osUnknown){
		switch (Key & ~k_Repeat) {
			case k0: HotkeyAction(0); break;
			case k1: HotkeyAction(1); break;
			case k2: HotkeyAction(2); break;
			case k3: HotkeyAction(3); break;
			case k4: HotkeyAction(4); break;
			case k5: HotkeyAction(5); break;
			case k6: HotkeyAction(6); break;
			case k7: HotkeyAction(7); break;
			case k8: HotkeyAction(8); break;
			case k9: HotkeyAction(9); break;
			case kBack:{
				return doBackAction();
			}
			case kRed:{
				return doBackAction();
			}
			case kBlue:{
				this->visFactory->shutdownOsd();
			}
			case kStop:
				return osEnd;
			default: {
				return osContinue;
			}
		}
	} else if(state == osUser1){
		Show();
	}
	return osContinue;
}

eOSState Visualizer::doBackAction(){
	if(this->visFactory->discardQueuedAction() == 0){
		return osEnd;
	} else {
		Show();
		return osContinue;
	}
	/*
	if( this->currentVisAction == NULL |
		this->visFactory->discardAction(this->currentVisAction) == 0){
		return osEnd;
	} else {
		Show();
		return osContinue;
	}
	*/
}

void Visualizer::HotkeyAction(int _aalItemHotkey){
	AAL_DEBUG(logger, "HotkeyAction, key: %d", _aalItemHotkey);
	AALItem * aalItem = this->visFactory->getController()->getRootAALItem()->getAALItemByHotkey(_aalItemHotkey);
	if(aalItem != NULL){
		this->visFactory->getController()->publish(doShow, "", aalItem);
	}
}
